package com.corpgovernment.common.utils;

import com.corpgovernment.common.apollo.BaseConfig;
import com.corpgovernment.common.common.CorpBusinessException;
import com.corpgovernment.common.enums.DefaultSupplierResponseStatus;
import com.corpgovernment.common.supplier.ResponseConverter;
import com.corpgovernment.common.supplier.ResponseConverterHolder;
import com.corpgovernment.common.supplier.ResultCodeMappingExceptionHolder;
import com.ctrip.corp.obt.generic.core.http.HttpResponse;
import com.ctrip.corp.obt.generic.utils.JsonUtils;
import com.ctrip.corp.obt.generic.utils.StringUtils;
import com.ctrip.corp.obt.metric.Metrics;
import com.ctrip.corp.obt.metric.log.model.MetricMeta;
import com.ctrip.corp.obt.metric.spectator.api.Id;
import lombok.AllArgsConstructor;
import lombok.extern.slf4j.Slf4j;
import org.springframework.stereotype.Component;

import java.io.IOException;
import java.util.Map;

import static com.ctrip.corp.obt.generic.constants.GenericConstants.UNKNOWN;
import static com.ctrip.corp.obt.generic.utils.Conditional.ofNullable;

/**
 * http调用供应商接口的客户端
 *
 * @author xukang zhang
 * @date 2024/2/8 13:52
 */
@Component
@AllArgsConstructor
@Slf4j
public class SupplierClient {
    /**
     * 分销错误信息转换器持有者
     */
    private final ResponseConverterHolder responseConverterHolder;
    /**
     * resultCode与对应异常的映射关系持有者
     */
    private final ResultCodeMappingExceptionHolder resultCodeMappingExceptionHolder;

    private static final String FAKE_ERROR_METRIC_ID_PREFIX = "supplier.client.invoke.fakeError";

    private static final Id FAKE_ERROR_METRIC_ID = Metrics.REGISTRY.createId(FAKE_ERROR_METRIC_ID_PREFIX);


    public <T> T doPostJSONUseSignReplaceSupplierCardNo(String supplier, String name, String requestUrl,
                                                        String requestBody, String userKey, Class<T> responseClazz, Map<String, Object>... maps) throws IOException {
        String responseBody = HttpUtils.doPostJSONUseSignReplaceSupplierCardNo(supplier, name, requestUrl, requestBody, userKey, maps);
        if (Boolean.FALSE.equals(BaseConfig.defaultConfig().enabledSupplierClient())) {
            // 开关关闭，直接返回响应体的json解析结果
            return degradation(responseClazz, responseBody);
        }
        try {
            return getDataOrThrowException(responseClazz, responseBody);
        } catch (CorpBusinessException e) {
            fakeErrorMetric(supplier, name, requestUrl, e, responseBody);
            throw e;
        } catch (Exception e) {
            log.error("SupplierClient invoke failed with exception", e);
            processExceptionMetric(supplier, name, requestUrl, e);
            throw e;
        }
    }


    /**
     * 成功则获取data，失败则抛出异常
     *
     * @param responseClazz
     * @param responseBody
     * @param <T>
     * @return
     */
    private <T> T getDataOrThrowException(Class<T> responseClazz, String responseBody) {
        if (StringUtils.isBlank(responseBody)) {
            return defaultFailureProcess();
        }
        HttpResponse<T> httpResponse = convertHttpResponse(responseClazz, responseBody);
        if (httpResponse == null) {
            return defaultFailureProcess();
        }
        if (isSuccess(httpResponse)) {
            // 成功，则返回data
            return httpResponse.getData();
        }
        // 失败，则抛出异常
        CorpBusinessException exception = resultCodeMappingExceptionHolder.getResponseConverter(httpResponse.getResultCode());
        if (exception != null) {
            throw exception;
        }
        throw new CorpBusinessException(httpResponse.getResultCode(), httpResponse.getMessage());
    }


    /**
     * 默认失败处理
     *
     * @param <T>
     * @return
     */
    private <T> T defaultFailureProcess() {
        throw new CorpBusinessException(DefaultSupplierResponseStatus.EMPTY_RESPONSE);
    }

    private <T> boolean isSuccess(HttpResponse<T> httpResponse) {
        return httpResponse != null && httpResponse.getResultCode() == 0;
    }

    /**
     * 降级处理 (直接返回响应体的json解析结果)
     *
     * @param responseClazz
     * @param responseBody
     * @param <T>
     * @return
     */
    private <T> T degradation(Class<T> responseClazz, String responseBody) {
        return JsonUtils.parse(responseBody, responseClazz);
    }


    /**
     * 将供应商的响应转换成统一的HttpResponse
     *
     * @param responseClazz
     * @param responseBody
     * @param <T>
     * @return
     */
    private <T> HttpResponse<T> convertHttpResponse(Class<T> responseClazz, String responseBody) {
        ResponseConverter<T> responseConverter = responseConverterHolder.getResponseConverter(responseClazz);
        HttpResponse<T> httpResponse = null;
        if (responseConverter != null) {
            return responseConverter.convertSupplierResponse(responseBody);
        }
        ResponseConverter<Object> defaultResponseConverter = responseConverterHolder.getResponseConverter(Object.class);
        if (defaultResponseConverter == null) {
            return null;
        }
        HttpResponse<?> genericHttpResponse = defaultResponseConverter.convertSupplierResponse(responseBody);
        if (genericHttpResponse == null) {
            return null;
        }
        return new HttpResponse<>(genericHttpResponse.getResultCode(), genericHttpResponse.getMessage(), JsonUtils.parse(responseBody, responseClazz));
    }

    /**
     * SupplierClient处理过程发生的非主动抛出的业务异常埋点
     *
     * @param supplier
     * @param name
     * @param requestUrl
     * @param e
     */
    private static void processExceptionMetric(String supplier, String name, String requestUrl, Exception e) {
        try {
            Metrics.REGISTRY.counter(FAKE_ERROR_METRIC_ID
                            .withTag("supplier", ofNullable(supplier).orElse(UNKNOWN))
                            .withTag("name", ofNullable(name).orElse(UNKNOWN))
                            .withTag("exception", e.getClass().getSimpleName()))
                    .increment();
        } catch (Exception ex) {
            log.error("SupplierClient processException Metric failed with exception", ex);
        }
    }

    /**
     * 非0的代表成功的错误码埋点
     *
     * @param supplier
     * @param name
     * @param requestUrl
     * @param e
     * @param responseBody
     */
    private static void fakeErrorMetric(String supplier, String name, String requestUrl, CorpBusinessException e, String responseBody) {
        String msg = e.getMsg();
        if (StringUtils.isBlank(msg)) {
            return;
        }
        if (!StringUtils.contains(msg, "成功")) {
            return;
        }
        try {
            Metrics.REGISTRY.counter(FAKE_ERROR_METRIC_ID
                            .withTag("errorCode", ofNullable(e.getResultCode()).map(String::valueOf).orElse(UNKNOWN))
                            .withTag("errorMessage", ofNullable(msg).orElse(UNKNOWN))
                            .withTag("supplier", ofNullable(supplier).orElse(UNKNOWN))
                            .withTag("name", ofNullable(name).orElse(UNKNOWN))
                            .withTag("requestUrl", ofNullable(requestUrl).orElse(UNKNOWN)))
                    .increment();
            Metrics.LOGGER.collect(new MetricMeta(FAKE_ERROR_METRIC_ID_PREFIX + ".log")
                    .withTag("errorCode", ofNullable(e.getResultCode()).map(String::valueOf).orElse(UNKNOWN))
                    .withTag("errorMessage", ofNullable(msg).orElse(UNKNOWN))
                    .withTag("supplier", ofNullable(supplier).orElse(UNKNOWN))
                    .withTag("name", ofNullable(name).orElse(UNKNOWN))
                    .withTag("requestUrl", ofNullable(requestUrl).orElse(UNKNOWN))
                    .setMessage(responseBody));
        } catch (Exception ex) {
            log.error("SupplierClient fakeError Metric failed with exception", ex);
        }
    }
}
